﻿using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;

namespace Cloak.Mvvm.Bindings
{
	/// <summary>
	/// A binding between a property on a source object and a property on a view model
	/// </summary>
	public sealed class PropertyBinding : ViewModelBinding
	{
		/// <summary>
		/// Creates a binding betwen the source and target properties specified by the getters
		/// </summary>
		/// <param name="sourceGetter">The getter for the source property</param>
		/// <param name="targetGetter">The getter for the target property</param>
		/// <returns>A binding between the source and target properties specified by the getters</returns>
		public static IViewModelBinding Between(Expression<Func<object>> sourceGetter, Expression<Func<object>> targetGetter)
		{
			Contract.Requires(sourceGetter != null);
			Contract.Requires(targetGetter != null);

			return new PropertyBinding(new PropertyReference(sourceGetter), new PropertyReference(targetGetter));
		}

		private readonly IPropertyReference _sourceProperty;
		private readonly IPropertyReference _targetProperty;

		/// <summary>
		/// Initializes a property binding with the specified source and target properties
		/// </summary>
		/// <param name="sourceProperty">A reference to the property on the source object</param>
		/// <param name="targetProperty">A reference to the property on the target object</param>
		public PropertyBinding(IPropertyReference sourceProperty, IPropertyReference targetProperty)
		{
			Contract.Requires(sourceProperty != null);
			Contract.Requires(targetProperty != null);

			_sourceProperty = sourceProperty;
			_targetProperty = targetProperty;

			_targetProperty.Changed += OnTargetPropertyChanged;
		}

		/// <summary>
		/// Reads from the source property and sets the target property
		/// </summary>
		protected override void BindFromSourceCore()
		{
			_targetProperty.Value = _sourceProperty.Value;
		}

		/// <summary>
		/// Reads from the target property and sets the source property
		/// </summary>
		protected override void BindToSourceCore()
		{
			_sourceProperty.Value = _targetProperty.Value;
		}

		private void OnTargetPropertyChanged(object sender, EventArgs e)
		{
			IsModified = _targetProperty.ValueDiffersFrom(_sourceProperty);
		}
	}
}